import hashlib
import numpy as np
from numpy.random import default_rng
import matplotlib.pyplot as plt

# 密钥流产生器
# PLM 映射函数
# x:初始状态值
# r:控制参数
# num_segments：分段的段数，控制系统的混沌程度和分段的粒度。

def piecewise_logistic_map(x, r, num_segments):
    segment_length = 1.0 / num_segments
    for i in range(num_segments):
        left_segment = i * segment_length
        right_segment = (i + 1) * segment_length
        if x > left_segment and  x < right_segment:
            if i % 2 == 0:
                x = num_segments * num_segments * r *(x - left_segment) * (right_segment - x)
            else:
                x = 1 - (num_segments * num_segments * r * (x - left_segment) * (right_segment - x))
            break
        elif x == i / num_segments and x != 1:
            x = x + 1.0 /(num_segments)
            break
        elif x == 1:
            x = x - 1.0 /(num_segments)
            break
    return x


def updata_r(r,x):
    a, b = 2, 4.0
    u =  (a + (b-a) * (r - a + x) % (b - a)) % b
    return u

# 将十进制小数转化为二进制
# decimal十进制随机数
def decimal_to_binary(decimal):
    binary = ""
    while decimal != 0:
        decimal *= 2
        if decimal >= 1:
            binary += "1"
            decimal -= 1
        else:
            binary += "0"
        if len(binary) > 32:  # 设置精度为32位，防止死循环
            break
    return binary


def binary_to_decimal(binary_str):
    decimal_value = sum(int(digit) * 2**(-i-1) for i, digit in enumerate(binary_str))
    # 返回十进制数的和
    return decimal_value


# generate_key_stream函数是生成密钥的函数，其参数包括：
# x：初始值值，Logistic映射的 初始状态值。
# r: 控制参数，r <[0,4]
def generate_key_stream(x, r, num_segments, key_length):
    key = []
    r_list=[]
    y = x
    # print("x:"+str(x))
    for j in range(key_length):
        y = np.mod(3.99 * y * (1 - y), 1)
        # y = piecewise_logistic_map(x, r, num_segments)
        x = piecewise_logistic_map(x, r, num_segments)
        t = xor(decimal_to_binary(x),decimal_to_binary(y))
        r = updata_r(r,x)
        # key.append(int(x * 10000) % 256) # 取最后两位小数作为密钥的一个字节
        key.append(binary_to_decimal(t))
        # key.append(x)
        # key.append(y)
        r_list.append(r)
        # print(r)
        # print(decimal_to_binary(x))
    return key

# 异或运算
# key_stream_1,key_stream_2两个二进制字符串密钥流
def xor(key_stream_1,key_stream_2):
    key_stream = ""
    for i in range(len(key_stream_1)):
        if key_stream_1[i] == key_stream_2[i]:
            key_stream += "0"
        else:
            key_stream += "1"
    # print("key_stream_1:"+key_stream_1)
    # print("key_stream_2:"+key_stream_2)
    # print("key_stream:"+key_stream)
    return key_stream

#  密钥获取hash函数
#  seed_str：密钥，用于初始化参数
def key_to_hash(seed_str):
    # 计算字符串的哈希值
    sha256 = hashlib.sha256(seed_str.encode()).digest()
    # 使用哈希值作为种子
    rng = default_rng(int.from_bytes(sha256, "big"))
    # 生成随机数
    r = rng.random()
    # 保留两位小数
    r = np.round(r, decimals=2)
    # 输出结果
    return r

# 绘制密度概率图
def showChart(data):
    # 计算概率分布
    counts, bins = np.histogram(data, bins=256)
    probs = counts / sum(counts) * 100

    # 绘制概率分布图
    plt.bar(bins[:-1], probs, width=0.004, alpha=0.7)
    # plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.rcParams['font.sans-serif'] = ['SimSun']  # 设置中文字体为宋体
    plt.rcParams['font.serif'] = ['Times New Roman']  # 设置西文字体为Times New Roman

    # 解决负号'-'显示为方块的问题
    plt.rcParams['axes.unicode_minus'] = False
    plt.ylim([0, 5.0])
    # plt.yticks([0,0.5,1.0,1.5,2.0])
    plt.yticks([0,0.5,1.0,1.5,2.0,2.5,3.0,3.5,4.0,4.5,5.0])

    plt.xlim([0, 1])
    # plt.xticks([0,25,50,75,100,125,150,175,200,225,250])
    plt.xticks([0,0.2,0.4,0.6,0.8,1.0])

    # 添加标题和标签
    # plt.title('logistic map')
    plt.xticks(fontsize=24)
    plt.yticks(fontsize=24)
    plt.xlabel('x',fontsize=24)
    plt.ylabel('百分比 (%)',fontsize=24)

    # 显示图像
    plt.show()

def showChart_r(data):
    # 计算概率分布
    counts, bins = np.histogram(data, bins=200)
    probs = counts / sum(counts) * 100

    # 绘制概率分布图
    plt.bar(bins[:-1], probs, width=0.0024, alpha=0.7)

    plt.ylim([0, 2.0])
    plt.yticks([0,0.5,1.0,1.5,2.0])
    # plt.yticks([0,1,2,3,4,5,6,7,8])

    plt.xlim([0, 4.0])
    # plt.xticks([0,25,50,75,100,125,150,175,200,225,250])
    plt.xticks([3.57,3.67,3.77,3.87,3.97,4.0])

    # 添加标题和标签
    # plt.title('Probability density diagram of control parameter μ')
    plt.xlabel('μ')
    plt.ylabel('Probability (%)')

    # 显示图像
    plt.show()


def ss_chart(keylen,data):
    plt.scatter(range(keylen), data, s=5)
    plt.rcParams['font.sans-serif'] = ['SimSun']  # 设置中文字体为宋体
    plt.rcParams['font.serif'] = ['Times New Roman']  # 设置西文字体为Times New Roman

    # 解决负号'-'显示为方块的问题
    plt.rcParams['axes.unicode_minus'] = False

    plt.xlabel('迭代次数',fontsize=24)
    plt.ylabel('状态值',fontsize=24)
    plt.ylim([0, 1.0])
    plt.xlim([0, 50000])
    plt.xticks(fontsize=24)
    plt.yticks(fontsize=24)
    # plt.title(f'Logistic Scatter Plot')
    plt.show()

if __name__ == '__main__':
    seed_str = "this is a key" #密钥key
    u = 26         #迭代状态值u->(0,4)
    n = 64            #映射的分段总数
    x = x = key_to_hash(seed_str)
    key_length = 50000 # key_length：生成的密钥长度，单位为字节，密钥流位数为8*key_length
    key = generate_key_stream(x, u, n, key_length)

    # data = [int(x,2) for x in key]

    print(key)
    print(len(key))
    # showChart(key)
    ss_chart(key_length,key)

    # showChart(key)
    # ss_chart(key_length,key)





   




